#ifndef DEAL_BUFFER_H
#define DEAL_BUFFER_H

#include <event2/buffer.h>
#include <stdio.h>

typedef void (*code_cb_t)(int index, uint8_t* buffer, size_t buffer_size);

template<typename MsgT>
bool evbuffer_send(
        evbuffer* output_,
        MsgT* &msg_,
        uint32_t* sended = NULL,
        int send_index = -1,
        code_cb_t code_cb = NULL) {

    if (NULL != sended)
        *sended = 0;

    uint32_t head_size = sizeof(uint32_t);
    uint32_t msg_size = msg_->ByteSize();
    evbuffer_iovec v[1];
    if (evbuffer_reserve_space(output_, head_size+msg_size, v, 1) < 1) {
        perror("buffer_t::send evbuffer_reserve_space");
        return false;
    }

    *(uint32_t*)v->iov_base = msg_size;
    msg_->SerializeToArray((char*)v->iov_base+head_size, msg_size);
    delete msg_;
    msg_ = NULL;

    if (send_index >= 0 && code_cb)
        code_cb(send_index, (uint8_t*)v->iov_base+head_size, msg_size);

    v->iov_len = head_size + msg_size;
    if (evbuffer_commit_space(output_, v, 1) < 0) {
        perror("buffer_t::send evbuffer_commit_space");
        return false;
    }

    if (NULL != sended)
        *sended = head_size + msg_size;

    return true;
}

template<typename MsgT>
MsgT* evbuffer_recv(
        evbuffer* input_,
        uint32_t* frame_size = NULL,
        int recv_index = -1,
        code_cb_t code_cb = NULL) {

    if (NULL != frame_size)
        *frame_size = 0;

    uint32_t head_size = sizeof(uint32_t);
    uint32_t msg_size = 0;

    uint32_t avalible = evbuffer_get_length(input_);
    if (avalible <= head_size)
        return NULL;

    if (NULL != frame_size)
        *frame_size = head_size + msg_size;

    if (evbuffer_copyout(input_, &msg_size, head_size) < head_size)
        return NULL;

    if (NULL != frame_size)
        *frame_size = head_size + msg_size;

    if (head_size + msg_size > avalible)
        return NULL;

    if (evbuffer_drain(input_, head_size) < 0) {
        perror("buffer_t::recv evbuffer_drain");
        return NULL;
    }

    uint8_t* array = evbuffer_pullup(input_, msg_size);
    if (recv_index >= 0 && code_cb)
        code_cb(recv_index, array, msg_size);

    auto msg = new MsgT;
    if (!msg->ParseFromArray(array, msg_size)) {
        delete msg;
        msg = NULL;
    }
    evbuffer_drain(input_, msg_size);

    return msg;
}

#endif
